package org.hit.burkun.swalk;

import java.util.ArrayList;
import java.util.HashMap;

import cern.colt.matrix.tdouble.DoubleMatrix1D;

class IntEdge {
	public int uid;
	public double score;

	public IntEdge(int uid, double score) {
		this.uid = uid;
		this.score = score;
	}
}

public class LinkedListNetwork extends RandomWalkGraph {

	protected double[] rowSums;
	protected ArrayList<HashMap<Integer, Double>> adjLinks;
	protected ArrayList<HashMap<Integer, Double>> QAdjLinks;
	protected ArrayList<HashMap<Integer, Double>> dQAdjLinks;
	protected boolean useWeiFunc = true;

	public LinkedListNetwork(){}
	public LinkedListNetwork(int n, int s, int f, ArrayList<FeatureField> list,
			ArrayList<Integer> D, ArrayList<Integer> L, boolean useWeiFunc) {
		super(n, s, f, list, D, L);
		rowSums = new double[n];
		this.useWeiFunc = useWeiFunc;
	}
	
	private void initAdj() {
		adjLinks = new ArrayList<>(dim);
		// init arr, 从0开始
		for (int i = 0; i < dim; i++) {
			adjLinks.add(i, new HashMap<Integer, Double>());
		}

	}

	private void initQadj() {
		QAdjLinks = new ArrayList<>(dim);
		// init arr, 从0开始
		for (int i = 0; i < dim; i++) {
			QAdjLinks.add(i, new HashMap<Integer, Double>());
		}
	}

	private void initDqAdj() {
		dQAdjLinks = new ArrayList<>(dim);
		// init arr, 从0开始
		for (int i = 0; i < dim; i++) {
			dQAdjLinks.add(i, new HashMap<Integer, Double>());
		}
	}

	@Override
	public void buildAdjacencyMatrix(DoubleMatrix1D param) {
		initAdj();
		// super.buildAdjacencyMatrix(param);
		for (FeatureField ff : list) {
			int c = ff.column;
			int r = ff.row;
			// reverse
			DoubleMatrix1D f = ff.features;
			double score = f.zDotProduct(param);
			if(useWeiFunc){
				score = weightingFunction(score);
			}
			adjLinks.get(c).put(r, score);
			adjLinks.get(r).put(c, score);
		}
		
	}

	@Override
	public ArrayList<HashMap<Integer, Double>> buildTransitionTranspose(
			double alpha) {
		// 改过来
		initQadj(); // 一定要这么做！
		// SparseCCDoubleMatrix2D Q = new SparseCCDoubleMatrix2D(
		// this.A.rows(), this.A.columns());
		// row sums
		int r, c;
		for (int i = 0; i < this.dim; rowSums[i++] = 0)
			;
		for (int i = 0; i < this.list.size(); i++) {
			r = this.list.get(i).row;
			c = this.list.get(i).column;
			rowSums[r] += this.adjLinks.get(r).get(c);
			if (r != c)
				rowSums[c] += this.adjLinks.get(c).get(r);
		}

		// (1-alpha) * A[i][j] / sumElements(A[i])) + 1(j == s) * alpha
		// build the transpose of Q
		double value;
		for (int i = 0; i < this.list.size(); i++) {
			r = this.list.get(i).row;
			c = this.list.get(i).column;
			value = this.adjLinks.get(r).get(c);
			value *= (1 - alpha);
			// value /= rowSums[r];
			// Q.set(c, r, value);
			QAdjLinks.get(c).put(r, value / rowSums[r]);

			if (r == c)
				continue;

			// value = this.A.get(c, r);
			// value *= (1 - alpha);
			// value /= rowSums[c];
			QAdjLinks.get(r).put(c, value / rowSums[c]);
		}

		for (int i = 0; i < QAdjLinks.size(); i++) {
			if (QAdjLinks.get(this.s).containsKey(i)) {
				value = QAdjLinks.get(this.s).get(i);
				value += alpha;
				QAdjLinks.get(this.s).put(i, value);
			} else {
				QAdjLinks.get(this.s).put(i, alpha);
			}
		}
		return QAdjLinks;
	}

	@Override
	public ArrayList<HashMap<Integer, Double>> transitionDerivativeTranspose(
			int featureIndex, double alpha,DoubleMatrix1D param) {
		initDqAdj();
		// SparseCCDoubleMatrix2D dQt = new SparseCCDoubleMatrix2D(this.dim,
		// this.dim);
		// System.out.println("transitionDerivativeTranspose...");
		// derivative row sums
		int r, c;
		double[] dRowSums = new double[this.dim];
		for (int i = 0; i < this.list.size(); i++) {
			r = this.list.get(i).row;
			c = this.list.get(i).column;
			double x = this.list.get(i).features.zDotProduct(param);
			double fs = list.get(i).features.get(featureIndex);
			double dw = weightingFunctionDerivative(x, fs);
			dRowSums[r] += dw;
			if (r != c)
				dRowSums[c] += dw;
		}

		double value;
		for (int i = 0; i < this.list.size(); i++) {
			r = this.list.get(i).row;
			c = this.list.get(i).column;
			double score = 0;
			if (adjLinks.get(r).containsKey(c)) {
				score = adjLinks.get(r).get(c);
			}
			double x = this.list.get(i).features.zDotProduct(param);
			double fs = list.get(i).features.get(featureIndex);
			double dw = weightingFunctionDerivative(x,fs);
			value = (dw * rowSums[r]) - (score * dRowSums[r]);
			value *= (1 - alpha);
			value /= Math.pow(rowSums[r], 2);
			dQAdjLinks.get(c).put(r, value);
			// dQt.set(c, r, value);

			if (c == r)
				continue;

			double score1 = 0;
			if (adjLinks.get(c).containsKey(r)) {
				score1 = adjLinks.get(c).get(r);
			}
			value = (dw * rowSums[c]) - (score1 * dRowSums[c]);
			value *= (1 - alpha);
			value /= Math.pow(rowSums[c], 2);
			// dQt.set(r, c, value);
			dQAdjLinks.get(r).put(c, value);
		}
		return dQAdjLinks;
	}

	@Override
	public double weightingFunction(double x) {
		// return Math.exp(x);
		if (x > 30)
			return 1.0;
		if (x < -30)
			return 0.0;
		return 1.0 / (1.0 + Math.exp(-x));
	}

	@Override
	public double weightingFunctionDerivative(double x, double featureValue) {
		if (x > -100 && x < 100) {
			double ex = Math.exp(x);
			return ex / ((1.0 + ex) * (1.0 + ex)) * (featureValue);
		} else {
			return 0.0;
		}
		// HashMap<Integer, Double> edges = adjLinks.get(row);
		// if(edges.containsKey(column)){
		// return edges.get(column) * edges.get(column) *
		// Math.exp(-featureValue);
		// }
		// return 0;
	}

	@Override
	public boolean hasLink(int from, int to) {
		HashMap<Integer, Double> edges = adjLinks.get(from);
		return edges.containsKey(to);
	}

	@Override
	public void pageRankAndGradient(DoubleMatrix1D param, double alpha,
			double eps) {
		//计算
		super.pageRankAndGradient(param, alpha, eps);
	}

}
